﻿using System;
using System.Collections.Generic;
using System.Linq;
using KryptecizerBase;
using System.Collections;

namespace Kryptonalysis.BletchleyPark
{
    public class StringDistanceTable
    {
        private List<StringDistances> _dists;
        private int _stringLength;
        private KryptoText kText;
        private SortedList<int, int> _DistanceTable;
        private int _minPWLength = 2;

        /// <summary>
        /// Gets the distance table.
        /// </summary>
        /// <value>The distance table.</value>
        public SortedList<int, int> DistanceTable
        {
            get { return _DistanceTable; }
        }

        /// <summary>
        /// Gets the distances.
        /// </summary>
        /// <value>The distances.</value>
        public List<int> Distances
        {
            get { return GetDistances(); }
        }

        /// <summary>
        /// Gets the distances.
        /// </summary>
        /// <returns></returns>
        private List<int> GetDistances()
        {
            List<int> result = new List<int>();
            IEnumerator<KeyValuePair<int, int>> Enumerator;
            Enumerator = _DistanceTable.GetEnumerator();
            
            while (Enumerator.MoveNext())
                result.Add((int)Enumerator.Current.Key);
            return result;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="StringDistanceTable"/> class.
        /// </summary>
        /// <param name="stringLength">Length of the string.</param>
        /// <param name="kryptoText">The krypto text.</param>
        /// <param name="handler">The handler.</param>
        public StringDistanceTable(int stringLength, KryptoText kryptoText, ProcessingEventHandler handler) 
            : this(stringLength, kryptoText)
        {
            this.Processing += handler;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="StringDistanceTable"/> class.
        /// </summary>
        /// <param name="stringLength">Length of the string.</param>
        /// <param name="kryptoText">The krypto text.</param>
        public StringDistanceTable(int stringLength, KryptoText kryptoText)
        {
            _stringLength = stringLength;
            kText = kryptoText;
            _dists = new List<StringDistances>();
            _DistanceTable = new SortedList<int, int>();
        }

        /// <summary>
        /// Evalautes the KryptoText.
        /// </summary>
        /// <returns></returns>
        public List<int> Evalaute()
        {
            FillDists(kText);
            /*foreach (StringDistances item in _dists)
                Console.WriteLine(item.ToString());*/
            FillTable();
            
            /* IDictionaryEnumerator Enumerator;
             Enumerator = _DistanceTable.GetEnumerator();
            
             while (Enumerator.MoveNext())
             {
                 Console.Write(Enumerator.Key.ToString() + "\t");
                 Console.WriteLine(Enumerator.Value.ToString());
             }*/

            return Distances;
        }

        /// <summary>
        /// Called when [prozessing].
        /// </summary>
        /// <param name="percent">The percent.</param>
        private void OnProzessing(int percent)
        {
            if (Processing != null)
                Processing(percent);
        }

        /// <summary>
        /// Handles the Processing event.
        /// </summary>
        public delegate void ProcessingEventHandler(int percentDone);
        /// <summary>
        /// Occurs when [processing].
        /// </summary>
        public event ProcessingEventHandler Processing;

        /// <summary>
        /// Fills the table.
        /// </summary>
        private void FillTable()
        {
            foreach (StringDistances _dist in _dists)
                AddDistsToTable(_dist);
        }

        /// <summary>
        /// Adds the distances to table.
        /// </summary>
        /// <param name="_dist">The _dist.</param>
        private void AddDistsToTable(StringDistances _dist)
        {
            foreach (int item in _dist.Distances)
                AddIntToTable(item);
        }

        /// <summary>
        /// Adds an int to table.
        /// </summary>
        /// <param name="dist">The dist.</param>
        private void AddIntToTable(int dist)
        {
            if (!_DistanceTable.ContainsKey(dist))
                _DistanceTable.Add(dist, 1);
            else
            {
                int val = (int)_DistanceTable[dist];
                val++;
                _DistanceTable[dist] = val;
            }
        }

        /// <summary>
        /// Fills the distances.
        /// </summary>
        /// <param name="kryptoText">The krypto text.</param>
        private void FillDists(KryptoText kryptoText)
        {
            string kryptString = kryptoText.Clone().GetAsString();
            kryptString = KryptoText.EleminateSpecialSigns(kryptString);
            string repeatingString, allreadyTested = String.Empty;
            double onePercent = kryptString.Length / 100d;
            int cnt = 0;
            while (kryptString.Length > _stringLength)
            {
                //asqwertzuioaspoiuasztxcghjkxcvbnxcas
                repeatingString = kryptString.Substring(0, _stringLength);
                kryptString = kryptString.Remove(0, _stringLength);
                if (!allreadyTested.Contains(repeatingString))
                {
                    string[] tmpSplits = kryptString.Split(new string[]{ repeatingString }, StringSplitOptions.RemoveEmptyEntries);
                    List<string> splits = tmpSplits.ToList();
                    if (!kryptString.EndsWith(repeatingString))
                        splits.Remove(splits[splits.Count - 1]);
                    AddToDists(splits, repeatingString);
                }
                allreadyTested += repeatingString;
                kryptString = (repeatingString + kryptString).Remove(0, 1);
                cnt++;
                OnProzessing((int)(cnt * onePercent));
            }
        }

        /// <summary>
        /// Adds in the text-splits to distances.
        /// </summary>
        /// <param name="splits">The splits.</param>
        /// <param name="repeatingString">The repeating string.</param>
        private void AddToDists(List<string> splits, string repeatingString)
        {
            List<int> di = new List<int>();
            foreach (string split in splits)
                if (split.Length > _minPWLength)
            	    di.Add(split.Length + repeatingString.Length);
            if(di.Count > 0)
                _dists.Add(new StringDistances(repeatingString, di));
        }
                                  
        
        
    }
}
